package magnet.processor.registry.instances;

import com.squareup.javapoet.ClassName;

import java.util.*;

public class Model {

    private Model() {

    }

    public static class Index {

        private List<Inst> instances;
        private List<Section> sections;

        public Index(List<Inst> instances, List<Section> sections) {
            this.instances = instances;
            this.sections = sections;
        }


        public void accept(IndexVisitor visitor) {
            visitor.visit(this);
            for (Section section : sections) {
                section.accept(visitor);
            }
        }

        public List<Inst> getInstances() {
            return instances;
        }

        public List<Section> getSections() {
            return sections;
        }
    }

    public static class Inst {

        private String type;
        private String classifier;
        private ClassName factory;

        public Inst(String type, String classifier, ClassName factory) {
            this.type = type;
            this.classifier = classifier;
            this.factory = factory;
        }

        public void accept(InstVisitor visitor) {
            visitor.visit(this);
        }

        public ClassName getFactory() {
            return factory;
        }

        public String getType() {
            return type;
        }

        public String getClassifier() {
            return classifier;
        }
    }

    public static class InstComparator implements Comparator<Inst> {
        @Override
        public int compare(Inst left, Inst right) {
            int c1 = left.type.compareTo(right.type);
            if (c1 != 0) {
                return c1;
            }
            int c2 = left.classifier.compareTo(right.classifier);
            if (c2 != 0) {
                return c2;
            }
            return left.factory.compareTo(right.factory);
        }
    }


    public interface InstVisitor {
        void visit(Inst inst);
    }

    public static class Range {

        private String type;
        private String classifier;
        private Inst inst;
        private int from;

        private List<Inst> impls;
        private ClassName firstFactory;

        public Range(String type, String classifier, Inst inst, int from) {
            this.type = type;
            this.classifier = classifier;
            this.inst = inst;
            this.from = from;
            impls = new ArrayList<>();
            impls.add(inst);
        }

        public ClassName getFirstFactory() {
            return impls.get(0).factory;
        }

        public void accept(IndexVisitor visitor) {
            visitor.visit(this);
            for (Inst inst : impls) {
                inst.accept(visitor);
            }
        }

        public int getFrom() {
            return from;
        }

        public List<Inst> getImpls() {
            return impls;
        }

        public String getClassifier() {
            return classifier;
        }

        public String getType() {
            return type;
        }
    }

    public static class Section {

        public String type;
        public Map<String, Range> ranges;
        private ClassName firstFactory;

        public Section(String type) {
            this.type = type;
            this.ranges = new LinkedHashMap<>();
        }

        public String getType() {
            return type;
        }

        public Map<String, Range> getRanges() {
            return ranges;
        }

        public void setType(String type) {
            this.type = type;
        }

        public void setRanges(Map<String, Range> ranges) {
            this.ranges = ranges;
        }

        public ClassName getFirstFactory() {
            Iterator<Map.Entry<String, Range>> iterator = ranges.entrySet().iterator();
            if (iterator.hasNext()) {
                return iterator.next().getValue().getFirstFactory();
            }
            return null;
        }

        public void accept(IndexVisitor visitor) {
            visitor.visit(this);
            Iterator<Map.Entry<String, Range>> iterator = ranges.entrySet().iterator();
            while (iterator.hasNext()) {
                iterator.next().getValue().accept(visitor);
            }
        }
    }

    public interface IndexVisitor extends InstVisitor {
        void visit(Index index);

        void visit(Section section);

        void visit(Range range);
    }
}
